﻿using System.Timers;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Timer = System.Timers.Timer;

namespace Devonline.Communication.Client;

/// <summary>
/// 通讯客户端
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class CommunicationClient
{
    private readonly ConnectionUrl _connectionUrl;
    protected bool _isRunning = false;
    protected readonly Timer _timer;
    protected readonly HubConnection _connection;
    protected readonly ILogger<CommunicationClient> _logger;
    protected readonly ClientSetting _clientSetting;
    /// <summary>
    /// 当连接服务器过程中执行的委托, 发生于刚发起连接请求时, 以及断线重连时
    /// </summary>
    protected Action? _connecting;
    /// <summary>
    /// 当和服务器建立连接时执行的委托
    /// </summary>
    protected Action? _connected;

    public CommunicationClient(ILogger<CommunicationClient> logger, ClientSetting clientSetting)
    {
        _logger = logger;
        _clientSetting = clientSetting;
        _connectionUrl = new(_clientSetting.ClientId, _clientSetting.Receiver);

        try
        {
            _timer = new Timer(_clientSetting.MonitorInterval * AppSettings.UNIT_THOUSAND)
            {
                Enabled = true,
                AutoReset = true
            };

            _logger.LogInformation("Communication Client {clientId} will connect {host} ", ClientId, Host);

            var builder = new HubConnectionBuilder().WithUrl(Url, HttpTransportType.WebSockets | HttpTransportType.LongPolling);

            _ = _clientSetting.TransportType switch
            {
                TransportType.Json => builder.AddJsonProtocol(config => config.PayloadSerializerOptions = AppSettings.JsonSerializerOptions),
                TransportType.MessagePack => builder.AddMessagePackProtocol(config => config.SerializerOptions = MessagePack.MessagePackSerializerOptions.Standard),
                _ => builder.AddNewtonsoftJsonProtocol(config => config.PayloadSerializerSettings = AppSettings.JsonSerializerSettings)
            };

            _connection = builder.WithAutomaticReconnect().Build();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Communication Client {clientId} initial error", ClientId);
            throw;
        }
    }

    #region 公开访问的成员
    /// <summary>
    /// IM 服务器地址
    /// </summary>
    public string? Host => _clientSetting.Host;
    /// <summary>
    /// IM 客户端编号
    /// </summary>
    public string? ClientId => _clientSetting.ClientId;
    /// <summary>
    /// 客户端本地链接地址
    /// </summary>
    public string Url => Host + AppSettings.CHAR_QUESTION + _connectionUrl;
    /// <summary>
    /// 链接状态
    /// </summary>
    public HubConnectionState ConnectionState => _connection.State;
    #endregion

    #region 外部使用的公开方法
    /// <summary>
    /// 启动通讯
    /// </summary>
    /// <param name="connecting">连接服务器过程中执行的委托, 可用于在连接状态发生变化时通知 UI</param>
    /// <param name="connected">连接到服务器后执行的委托, 可用于在连接状态发生变化时通知 UI</param>
    /// <returns></returns>
    public virtual async Task StartAsync(Action? connecting = null, Action? connected = null)
    {
        try
        {
            _connecting = connecting;
            _connected = connected;

            _isRunning = true;

            //断线重连
            _connection.Reconnecting += OnConnectingAsync;

            //关闭重连
            _connection.Closed += OnConnectingAsync;

            //重新连上
            _connection.Reconnected += OnConnectedAsync;

            //启动监听
            _timer.Elapsed += MonitorAsync;
            _timer.Start();

            //启动连接
            await ConnectAsync();
        }
        catch (Exception ex)
        {
            _isRunning = false;
            _timer.Stop();
            _timer.Elapsed -= MonitorAsync;
            _timer.Dispose();
            _connection.Reconnecting -= OnConnectingAsync;
            _connection.Closed -= OnConnectingAsync;
            _connection.Reconnected -= OnConnectedAsync;
            _logger.LogError(ex, "Communication Client {clientId} start to connect server {host} error", ClientId, Host);
            throw;
        }
    }
    /// <summary>
    /// 关闭通讯
    /// </summary>
    /// <returns></returns>
    public virtual async Task StopAsync()
    {
        try
        {
            _isRunning = false;
            _timer.Stop();
            _timer.Elapsed -= MonitorAsync;
            _timer.Dispose();
            _connection.Reconnecting -= OnConnectingAsync;
            _connection.Closed -= OnConnectingAsync;
            _connection.Reconnected -= OnConnectedAsync;
            await _connection.StopAsync();
            await _connection.DisposeAsync();
            _logger.LogWarning("Communication Client {clientId} now closed the server {host}", ClientId, Host);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Communication Client {clientId} close the server {host} error", ClientId, Host);
            throw;
        }
    }
    /// <summary>
    /// 暂停通讯
    /// </summary>
    public virtual void Pause()
    {
        _isRunning = false;
        _timer.Stop();
        _logger.LogWarning("The Communication Client {clientId} now was paused", ClientId);
    }
    /// <summary>
    /// 继续通讯
    /// </summary>
    public virtual void Continue()
    {
        _logger.LogWarning("The Communication Client {clientId} now will continue", ClientId);
        _isRunning = true;
        _timer.Start();
    }

    /// <summary>
    /// 注册默认接受消息的方法
    /// 此处的数据对象可以接受 IEnumerable<T> 集合数据, 及 PagedResult<T> 传递分页的数据
    /// </summary>
    /// <param name="action"></param>
    public virtual void OnReceive<T>(Action<T>? action) => On("Receive", action);

    /// <summary>
    /// 发送数据到默认接收者
    /// 此处的数据对象可以接受 IEnumerable<T> 集合数据, 及 PagedResult<T> 传递分页的数据
    /// </summary>
    /// <param name="t">待发送的数据</param>
    /// <returns></returns>
    public virtual Task SendAsync<T>(T t) => InvokeAsync("Send", t);
    /// <summary>
    /// 发送数据到指定接收者
    /// 此处的数据对象可以接受 IEnumerable<T> 集合数据, 及 PagedResult<T> 传递分页的数据
    /// </summary>
    /// <param name="t">待发送的数据</param>
    /// <param name="receiver">接收者</param>
    /// <returns></returns>
    public virtual Task SendAsync<T>(T t, string receiver) => InvokeAsync("SendTo", t, receiver);
    #endregion

    #region 可继承的内部方法
    /// <summary>
    /// 当链接关闭时执行的事件处理委托方法
    /// </summary>
    /// <param name="exception"></param>
    /// <returns></returns>
    protected virtual async Task OnConnectingAsync(Exception? exception)
    {
        if (!_isRunning)
        {
            return;
        }

        _logger.LogError(exception, "Communication Client {clientId} disconnected from the {host} with exception {exception}, and will reconnect later", ClientId, Host, exception?.GetMessage());
        if (_connecting != null)
        {
            await Task.Run(_connecting);
        }

        //发起连接
        await ConnectAsync();
    }
    /// <summary>
    /// 当建立连接时执行的事件处理委托
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    protected virtual async Task OnConnectedAsync(string? id)
    {
        if (!_isRunning)
        {
            return;
        }

        _logger.LogWarning("Communication Client {clientId} now reconnected to the server {host}", ClientId, Host);
        if (_connected != null)
        {
            await Task.Run(_connected);
        }
    }

    /// <summary>
    /// 客户端连接异常关闭时, 自动发起重新连接
    /// </summary>
    /// <param name="exception"></param>
    /// <returns></returns>
    protected virtual async Task ConnectAsync()
    {
        if (_isRunning && _connection.State == HubConnectionState.Disconnected)
        {
            _logger.LogWarning("Communication Client {clientId} now will connect to the server {host}", ClientId, Host);
            await _connection.StartAsync();

            //连接成功以后
            if (_connection.State == HubConnectionState.Connected)
            {
                _logger.LogWarning("Communication Client {clientId} now has been connected to the server {host}", ClientId, Host);
            }
        }
    }
    /// <summary>
    /// 客户端连接监控
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    protected virtual async void MonitorAsync(object? sender, ElapsedEventArgs args) => await ConnectAsync();

    /// <summary>
    /// 注册客户端方法, 接收来自服务器的数据
    /// </summary>
    /// <param name="methodName">服务器方法名</param>
    /// <param name="action">处理接收数据的委托</param>
    protected virtual void On<T>(string methodName, Action<T>? action)
    {
        _connection.Remove(methodName);
        _connection.On<T>(methodName, t =>
        {
            try
            {
                if (!_isRunning)
                {
                    throw new Exception("The Communication Client now was not running");
                }

                _logger.LogDebug("Communication Client {clientId} trigger the method {methodName} with the data {data}", ClientId, methodName, t.ToJsonString());
                action?.Invoke(t);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Communication Client {clientId} trigger the method {methodName} with the data {data} throw exception", ClientId, methodName, t.ToJsonString());
            }
        });
    }
    /// <summary>
    /// 默认的调用服务器无惨的执行方法
    /// </summary>
    /// <param name="methodName"></param>
    /// <returns></returns>
    protected virtual Task InvokeAsync(string methodName)
    {
        if (!_isRunning)
        {
            throw new Exception("The Communication Client now was not running");
        }

        _logger.LogInformation("Communication Client {clientId} will call the method {methodName}", ClientId, methodName);
        return _connection.InvokeAsync(methodName);
    }
    /// <summary>
    /// 默认的调用服务器的执行方法
    /// </summary>
    /// <param name="methodName"></param>
    /// <param name="t"></param>
    /// <returns></returns>
    protected virtual Task InvokeAsync<T>(string methodName, T t)
    {
        if (!_isRunning)
        {
            throw new Exception("The Communication Client now was not running");
        }

        _logger.LogInformation("Communication Client {clientId} will call the method {methodName} with the data {data}", ClientId, methodName, t.ToJsonString());
        return _connection.InvokeAsync<T>(methodName, t);
    }
    /// <summary>
    /// 默认的调用服务器的执行方法
    /// </summary>
    /// <param name="methodName"></param>
    /// <param name="t"></param>
    /// <param name="receiver">接收者</param>
    /// <returns></returns>
    protected virtual Task InvokeAsync<T>(string methodName, T t, string receiver)
    {
        if (!_isRunning)
        {
            throw new Exception("The Communication Client now was not running");
        }

        _logger.LogInformation("Communication Client {clientId} will call the method {methodName} with the data {data} to receiver {receiver}", ClientId, methodName, t.ToJsonString(), receiver);
        return _connection.InvokeAsync<T>(methodName, t, receiver);
    }
    #endregion

    /// <summary>
    /// url connector
    /// </summary>
    record ConnectionUrl(string? ClientId, string? Receiver)
    {
        /// <summary>
        /// 重写的转字符串方法返回 url 字符串
        /// </summary>
        /// <returns></returns>
        public override string? ToString() => this.ToKeyValuePairs().ToString(true);
    }
}